﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using Xceed.Silverlight.Data.Stats;

namespace Xceed.Silverlight.Data
{
  public abstract class SimpleSyncIndexDataSourceProvider : SyncIndexDataSourceProvider
  {
    #region Constructor

    protected SimpleSyncIndexDataSourceProvider()
      : base()
    {
      this.InitializeFeatures();
    }

    #endregion

    #region CanFilterGroup Property

    public override bool CanFilterGroup
    {
      get
      {
        return m_canFilterGroup;
      }
    }

    private bool m_canFilterGroup; //false

    #endregion

    #region CanFind Property

    public override bool CanFind
    {
      get
      {
        return m_canFind;
      }
    }

    private bool m_canFind; //false

    #endregion

    #region CanGroupCount Property

    public override bool CanGroupCount
    {
      get
      {
        return m_canGroupCount;
      }
    }

    private bool m_canGroupCount; //false

    #endregion

    #region CanCalculateStats Property

    public override bool CanCalculateStats
    {
      get
      {
        return m_canCalculateStats;
      }
    }

    private bool m_canCalculateStats; //false

    #endregion

    #region Count Protected Property

    protected abstract long Count
    {
      get;
    }

    #endregion

    protected virtual long GetCount( IEnumerable<FieldValue> fieldValues )
    {
      throw new NotSupportedException();
    }

    protected abstract IEnumerable GetRange( long index, int count );

    protected abstract IEnumerable GetRange( IEnumerable<SortDescription> sortOrder, long index, int count );

    protected virtual IEnumerable<long> GetGroupCount( IEnumerable<SortDescription> sortOrder, long index, int count )
    {
      throw new NotSupportedException();
    }

    protected virtual IEnumerable<long> GetGroupCount( IEnumerable<SortDescription> sortOrder, IEnumerable<FieldValue> fieldValues, long index, int count )
    {
      throw new NotSupportedException();
    }

    protected virtual StatResult GetStatResult( StatFunction statFunction )
    {
      throw new NotSupportedException();
    }

    protected virtual StatResult GetStatResult( StatFunction statFunction, IEnumerable<FieldValue> fieldValues )
    {
      throw new NotSupportedException();
    }

    protected virtual FindResult Find( IEnumerable<SortDescription> sortOrder, IEnumerable<FindValue> fieldValues, FindPosition position )
    {
      throw new NotSupportedException();
    }

    protected sealed override CountQueryResult ExecuteCountQuery( CountQueryParameters queryParameters )
    {
      long count;

      if( queryParameters.FilterGroup != null )
      {
        IEnumerable<FieldValue> fieldValues = this.GetFieldValues(
                                                queryParameters.Sort.SortDescriptions,
                                                queryParameters.FilterGroup.Values ).ToList();

        count = this.GetCount( fieldValues );
      }
      else
      {
        count = this.Count;
      }

      return new CountQueryResult( count );
    }

    protected sealed override DataQueryResult ExecuteDataQuery( DataQueryParameters queryParameters )
    {
      // Retrieve the query parameters.
      IEnumerable<SortDescription> sortOrder = ( queryParameters.Sort != null ) ? queryParameters.Sort.SortDescriptions : null;
      long index = ( queryParameters.Skip != null ) ? queryParameters.Skip.Count : 0;
      int count = ( queryParameters.Take != null ) ? queryParameters.Take.Count : int.MaxValue;
      IEnumerable dataList;

      // Retrieve the requested data items.
      if( sortOrder != null )
      {
        dataList = this.GetRange( sortOrder, index, count );
      }
      else
      {
        dataList = this.GetRange( index, count );
      }

      long totalCount = this.Count;

      return new DataQueryResult( dataList, null, totalCount );
    }

    protected sealed override FindQueryResult ExecuteFindQuery( FindQueryParameters queryParameters )
    {
      Debug.Assert( ( queryParameters.Sort != null ) && ( queryParameters.Find != null ) );

      // Retrieve the query parameters.
      IEnumerable<SortDescription> sortOrder = queryParameters.Sort.SortDescriptions;
      IEnumerable<FindValue> fieldValues = queryParameters.Find.Values;
      FindPosition position = queryParameters.Find.Position;

      FindResult result = this.Find( sortOrder, fieldValues, position );

      return new FindQueryResult( result );
    }

    protected sealed override GroupCountQueryResult ExecuteGroupCountQuery( GroupCountQueryParameters queryParameters )
    {
      IEnumerable<SortDescription> sortOrder = queryParameters.Sort.SortDescriptions;
      IEnumerable<long> groupCount;
      long index = ( queryParameters.Skip != null ) ? queryParameters.Skip.Count : 0;
      int length = queryParameters.Take.Count;

      if( queryParameters.FilterGroup != null )
      {
        IEnumerable<FieldValue> fieldValues = this.GetFieldValues(
                                                queryParameters.Sort.SortDescriptions,
                                                queryParameters.FilterGroup.Values ).ToList();

        groupCount = this.GetGroupCount( sortOrder, fieldValues, index, length );
      }
      else
      {
        groupCount = this.GetGroupCount( sortOrder, index, length );
      }

      return new GroupCountQueryResult( groupCount );
    }

    protected sealed override StatQueryResult ExecuteStatQuery( StatQueryParameters queryParameters )
    {
      StatFunction statFunction = queryParameters.Function.StatFunction;
      StatResult statResult;

      if( queryParameters.FilterGroup != null )
      {
        IEnumerable<FieldValue> fieldValues = this.GetFieldValues(
                                                queryParameters.Sort.SortDescriptions,
                                                queryParameters.FilterGroup.Values ).ToList();

        statResult = this.GetStatResult( statFunction, fieldValues );
      }
      else
      {
        statResult = this.GetStatResult( statFunction );
      }

      return new StatQueryResult( statResult );
    }

    private IEnumerable<FieldValue> GetFieldValues(
      IEnumerable<SortDescription> sortOrder,
      IEnumerable<FilterGroupValue> values )
    {
      return sortOrder.Zip( values, ( sort, value ) => new FieldValue( sort.PropertyName, value.Value ) );
    }

    private void InitializeFeatures()
    {
      Type dataSourceProviderType = this.GetType();
      bool groupLevelCountImplemented = DataUtils.IsMethodImplemented(
                                          DataUtils.GetMethodDefinition<IEnumerable<FieldValue>, long>(
                                            ( fieldValues ) => this.GetCount( fieldValues ) ),
                                          dataSourceProviderType );
      bool allLevelGroupCountImplemented = DataUtils.IsMethodImplemented(
                                             DataUtils.GetMethodDefinition<IEnumerable<SortDescription>, long, int, IEnumerable<long>>(
                                               ( sortOrder, index, count ) => this.GetGroupCount( sortOrder, index, count ) ),
                                             dataSourceProviderType );
      bool groupLevelGroupCountImplemented = DataUtils.IsMethodImplemented(
                                               DataUtils.GetMethodDefinition<IEnumerable<SortDescription>, IEnumerable<FieldValue>, long, int, IEnumerable<long>>(
                                                 ( sortOrder, fieldValues, index, count ) => this.GetGroupCount( sortOrder, fieldValues, index, count ) ),
                                               dataSourceProviderType );
      bool allLevelStatImplemented = DataUtils.IsMethodImplemented(
                                       DataUtils.GetMethodDefinition<StatFunction, StatResult>(
                                         ( statFunction ) => this.GetStatResult( statFunction ) ),
                                       dataSourceProviderType );
      bool groupLevelStatImplemented = DataUtils.IsMethodImplemented(
                                         DataUtils.GetMethodDefinition<StatFunction, IEnumerable<FieldValue>, StatResult>(
                                           ( statFunction, fieldValues ) => this.GetStatResult( statFunction, fieldValues ) ),
                                         dataSourceProviderType );
      bool findImplemented = DataUtils.IsMethodImplemented(
                               DataUtils.GetMethodDefinition<IEnumerable<SortDescription>, IEnumerable<FindValue>, FindPosition, FindResult>(
                                 ( sortOrder, findValues, position ) => this.Find( sortOrder, findValues, position ) ),
                               dataSourceProviderType );

      m_canFind = findImplemented;
      m_canGroupCount = m_canFind && allLevelGroupCountImplemented;
      m_canCalculateStats = allLevelStatImplemented;

      m_canFilterGroup = ( groupLevelCountImplemented )
                      && ( !m_canGroupCount || groupLevelGroupCountImplemented )
                      && ( !m_canCalculateStats || groupLevelStatImplemented );
    }
  }
}
